﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using FluentPS.Consts;
using FluentPS.Extensions;
using FluentPS.Mapping;
using FluentPS.WebSvcResource;
using Microsoft.Office.Project.Server.Library;
using Resource = FluentPS.WebSvcResource.Resource;

namespace FluentPS.Services.Impl
{
    public class PSResourceService<T> : IPSResourceService<T> where T : class, new()
    {
        internal const string WebServiceUrl = "resource.asmx";
        private readonly ILogService _logService;
        private readonly ISessionService _sessionService;
        private readonly Resource _wssResource;

        public PSResourceService(ILogService logService, ISessionService sessionService)
        {
            _logService = logService;
            _sessionService = sessionService;
            _wssResource = new Resource();

            Init();
        }

        private void Init()
        {
            _wssResource.Url = _sessionService.PsiBaseUrl + WebServiceUrl;
            _wssResource.UseDefaultCredentials = true;
        }

        public T Get(Guid entityUid)
        {
            return Get(entityUid, true);
        }

        public T Get(Guid entityUid, bool loadCustomFields)
        {
            try
            {
                using (DataSet dsResource = _wssResource.ReadResource(entityUid))
                {
                    if (dsResource != null && dsResource.Tables[PsDataTableNames.Resources].Rows.Count > 0)
                        return ReadResourceFromDataSet(entityUid, dsResource, loadCustomFields);
                }
            }
            catch(Exception ex)
            {
                _logService.Error("Read resource exception: " + ex.Message);
                throw;
            }
            return null;
        }        

        public void Update(T entity)
        {
            var propertiesToUpdate = new List<PropertyInfo>();
            typeof(T).ForeachProperty(propertiesToUpdate.Add);

            var entityDataMapper = new EntityDataMapper<T>(_logService);
            var entityUid = entityDataMapper.GetEntityUid<Guid>(entity);
            if (entityUid == Guid.Empty)
                throw new Exception("Invalid entity UID");

            using (var dsResource = _wssResource.ReadResource(entityUid))
            {
                if (dsResource.Tables[PsDataTableNames.Resources].Rows.Count > 0)
                {
                    entityDataMapper.MapEntityNativeFieldsToDataRow(
                        entity,
                        dsResource.Tables[PsDataTableNames.Resources].Rows[0]);

                    entityDataMapper.MapCustomFieldsToDataSet(entity,
                        entityUid,
                        dsResource.Tables[PsDataTableNames.ResourceCustomFields],
                        propertiesToUpdate, new ResourceCustomFieldsMappingInfo());
                }

                _wssResource.UpdateResources(dsResource, false, false);
            }
        }

        public Guid GetResourceUidByUserAccount(string userAccount)
        {
            var filter = new Filter { FilterTableName = PsDataTableNames.Resources };
            filter.Fields.Add(new Filter.Field(ResourceCustomFieldsColumnNames.RES_UID));

            var op = new Filter.FieldOperator(
                            Filter.FieldOperationType.Equal,
                            ResourceCustomFieldsColumnNames.WRES_ACCOUNT, userAccount);
            filter.Criteria = op;

            using (var resourcesDs = (_wssResource.ReadResources(filter.GetXml(), false)))
            {
                if (resourcesDs.Resources.Rows.Count == 0)
                    return Guid.Empty;
                return (Guid)resourcesDs.Resources.Rows[0][ResourceCustomFieldsColumnNames.RES_UID];
            }
        }

        public T ReadResourceFromDataSet(Guid entityUid, DataSet dsResource, bool loadCustomFields)
        {
            return ReadResourceFromDataSet(entityUid, dsResource, loadCustomFields, DataRowVersion.Current);
        }

        public T ReadResourceFromDataSet(Guid entityUid, DataSet dsResource, bool loadCustomFields, DataRowVersion dataRowVersion)
        {
            if (dsResource == null ||
                !dsResource.Tables.Contains(PsDataTableNames.Resources) ||
                dsResource.Tables[PsDataTableNames.Resources].Rows.Count == 0)
                return null;

            var resourceRow = dsResource.Tables[PsDataTableNames.Resources].Rows[0];

            var resource = new T();
            var entityDataMapper = new EntityDataMapper<T>(_logService);
            entityDataMapper.MapDataRowToEntityNativeFields(resourceRow, resource, dataRowVersion);

            if (loadCustomFields)
                entityDataMapper.MapCustomFieldsToEntity(
                    resource,
                    entityUid,
                    dsResource.Tables[PsDataTableNames.ResourceCustomFields],
                    dataRowVersion,
                    new ResourceCustomFieldsMappingInfo());

            return resource;
        }        
    }
}
